<?php

/**
 * Class for handling images.
 *
 * Uses the GD PHP extension for manipulating images.
 *
 * @author Kovács Tamás
 * @category Smithy
 * @package Smithy_Image
 */
abstract class Smithy_Image_Abstract
{
    protected $_fileName = null;
    protected $_type = null;
    protected static $_componentPrefix = 'Smithy_Image';
    protected $_destination = null;
    protected $_width = 0;
    protected $_height = 0;

    /**
     * The image resource.
     *
     * @var resource
     */
    protected $_imageResource = false;


    /**
     * Constructor
     *
     * @param string $filename [OPTIONAL]
     * @return void
     */
    protected function __construct($filename = null)
    {
        $this->_fileName = (false === realpath($filename)) ? null : realpath($filename);
        if (null !== $this->_fileName) {
            $this->_loadImageFromFile();
        }
        $this->setDestination($this->_fileName);
    }

    /**
     * Returns a Smithy_Image instance based on the $type parameter.
     *
     * @param string $type
     * @param string $filename [OPTIONAL]
     * @return Smithy_Image
     */
    public static function getInstance($type = 'jpg', $fileName = null)
    {
        $type = str_replace(array('.', '/', "\\"), '', $type);
        $className = self::$_componentPrefix . '_' . ucfirst(strtolower($type));
        require_once realpath(dirname(__FILE__)) . '/' . ucfirst(strtolower($type)) . '.php';
        return new $className($fileName);
    }

    /**
     * Returns a Smithy_Image_Jpg instance.
     *
     * @param string $filename [OPTIONAL]
     * @return Smithy_Image_Jpg
     */
    public static function getJpgInstance($fileName = null)
    {
        return self::getInstance('jpg', $fileName);
    }

    /**
     * Loads the image's size.
     *
     * @return Smithy_Image_Abstract
     */
    protected function _loadImageSize()
    {
        list($this->_width, $this->_height) = getimagesize($this->_fileName);
        return $this;
    }

    /**
     * Computes the resized width and height of the image.
     *
     * @param int $width
     * @param int $height
     * @return array Width and height
     */
    protected function _getResizedDataKeepAspectRatio($width, $height)
    {
        $widthOrig = $this->getWidth();
        $heightOrig = $this->getHeight();

        if (abs(1 - $width / $widthOrig) > abs(1 - $height / $heightOrig)) {
            $height = ($width / $widthOrig) * $heightOrig;
        } else {
            $width = ($height / $heightOrig) * $widthOrig;
        }

        return array($width, $height);
    }

    /**
     * Resizes the image.
     *
     * The options array contains the options on how to resize the image.
     * Available options: width (int), height (int), keepAspectRatio (bool)
     *
     * @param array $options
     * @return Smithy_Image_Jpg
     */
    public function resize($options)
    {
        $default = array('width' => $this->getWidth(), 'height' => $this->getHeight(), 'keepAspectRatio' => true);
        $options = array_merge($default, $options);

        if (true === $options['keepAspectRatio']) {
            list($options['width'], $options['height']) = $this->_getResizedDataKeepAspectRatio($options['width'], $options['height']);
        }

        $newImageResource = imagecreatetruecolor($options['width'], $options['height']);
        imagecopyresampled($newImageResource, $this->_imageResource, 0, 0, 0, 0, $options['width'], $options['height'], $this->getWidth(), $this->getHeight());
        $this->_imageResource = $newImageResource;
        $this->_width = $options['width'];
        $this->_height = $options['height'];

        return $this;
    }

    /**
     * Sets the destination where to save the image.
     *
     * @param string $destination [OPTIONAL]
     * @return Smithy_Image_Abstract
     */
    public function setDestination($destination = null)
    {
        if (null === $destination) {
            $this->_destination = $this->_fileName;
        } else {
            $this->_destination = $destination;
        }
        return $this;
    }

    /**
     * Returns the image's width.
     *
     * @return int
     */
    public function getWidth()
    {
        return $this->_width;
    }

    /**
     * Returns the image's height.
     *
     * @return int
     */
    public function getHeight()
    {
        return $this->_height;
    }

    /**
     * Loads the image from file.
     *
     * @return Smithy_Image_Abstract
     */
    protected abstract function _loadImageFromFile();

    /**
     * Saves the image to the given destination.
     *
     * @param string $destination [OPTIONAL]
     * @return bool
     */
    public abstract function save($destination = null);

}
